
package com.shiku.imserver;


import com.shiku.imserver.cluster.ImClusterServiceIfc;
import com.shiku.imserver.common.message.AbstractMessage;
import com.shiku.imserver.common.message.ChatMessage;
import com.shiku.imserver.common.message.ErrorMessage;
import com.shiku.imserver.common.packets.ImPacket;
import com.shiku.imserver.common.utils.Callback;
import com.shiku.imserver.common.utils.ThreadUtil;
import com.shiku.imserver.message.MessageFactory;
import com.shiku.imserver.service.IMBeanUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.cluster.TioClusterConfig;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.Tio;
import org.tio.core.intf.Packet;
import org.tio.utils.lock.SetWithLock;


public class CoreService {
    private static Logger log = LoggerFactory.getLogger(CoreService.class);


    public static String parseResource(String jid) {

        if (jid == null) {
            return null;
        }


        int slashIndex = jid.indexOf('/');

        if (slashIndex + 1 > jid.length() || slashIndex < 0) {

            return "";

        }

        return jid.substring(slashIndex + 1);

    }


    public static String parseBareUserId(String jid) {

        int slashIndex = jid.indexOf('/');

        if (slashIndex < 0) {
            return jid;
        }

        if (slashIndex == 0) {

            return "";

        }

        return jid.substring(0, slashIndex);

    }


    public static void bindUser(ChannelContext channelContext, String userid) {

        String baseId = "";

        channelContext.groupContext.users.bind(userid, channelContext);

    }


    public static boolean send(ChannelContext channelContext, ImPacket packet) {

        return Tio.send(channelContext, (Packet) packet).booleanValue();

    }


    public static boolean bSend(ChannelContext channelContext, ImPacket packet) {

        return Tio.bSend(channelContext, (Packet) packet).booleanValue();

    }


    public static Boolean sendToUser(GroupContext groupContext, String userid, ImPacket packet) {

        return sendToUser(groupContext, userid, packet, false);

    }


    private static Boolean sendToUser(GroupContext groupContext, String userid, ImPacket packet, boolean isBlock) {

        SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);

        try {

            if (setWithLock == null) {

                return Boolean.valueOf(false);

            }


            ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();

            readLock.lock();

            try {

                Set<ChannelContext> set = (Set<ChannelContext>) setWithLock.getObj();

                boolean ret = false;

                for (ChannelContext channelContext : set) {

                    boolean singleRet = false;


                    if (isBlock) {

                        singleRet = bSend(channelContext, packet);

                    } else {

                        singleRet = send(channelContext, packet);

                    }

                    if (singleRet) {

                        ret = true;

                    }

                }

                return Boolean.valueOf(ret);

            } catch (Throwable e) {

                log.error(e.getMessage(), e);

            } finally {

                readLock.unlock();

            }

            return Boolean.valueOf(false);

        } finally {

            if (groupContext.isCluster() && !packet.isFromCluster()) {

                TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();


                if (tioClusterConfig.isCluster4user()) {


                    Tio.notifyClusterForUser(groupContext, userid, (Packet) packet);

                }

            }

        }

    }


    public static Boolean sendToUserOtherResource(GroupContext groupContext, String userid, String channelContextId, ImPacket packet, boolean isBlock) {

        SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);

        try {

            if (setWithLock == null) {

                return Boolean.valueOf(false);

            }


            if (1 == setWithLock.size()) {

                return Boolean.valueOf(true);

            }

            ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();

            readLock.lock();

            try {

                Set<ChannelContext> set = (Set<ChannelContext>) setWithLock.getObj();

                boolean ret = false;

                for (ChannelContext channelContext : set) {


                    if (channelContext.getId().equals(channelContextId)) {
                        continue;
                    }

                    boolean singleRet = false;


                    if (isBlock) {

                        singleRet = bSend(channelContext, packet);

                    } else {

                        singleRet = send(channelContext, packet);

                    }

                    if (singleRet) {

                        ret = true;

                    }

                }

                return Boolean.valueOf(ret);

            } catch (Throwable e) {

                log.error(e.getMessage(), e);

            } finally {

                readLock.unlock();

            }

            return Boolean.valueOf(false);

        } finally {

            if (groupContext.isCluster() && !packet.isFromCluster()) {

                TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();


                if (tioClusterConfig.isCluster4user()) {


                    Tio.notifyClusterForUser(groupContext, userid, (Packet) packet);

                }

            }

        }

    }


    public static Boolean sendToUserResource(GroupContext groupContext, String userid, String channelContextId, ImPacket packet, boolean isBlock) {

        SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);

        try {

            if (setWithLock == null) {

                return Boolean.valueOf(false);

            }


            if (1 == setWithLock.size()) {

                return Boolean.valueOf(true);

            }

            ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();

            readLock.lock();

            try {

                Set<ChannelContext> set = (Set<ChannelContext>) setWithLock.getObj();

                boolean ret = false;

                for (ChannelContext channelContext : set) {


                    if (channelContext.getId().equals(channelContextId)) {
                        continue;
                    }

                    boolean singleRet = false;


                    if (isBlock) {

                        singleRet = bSend(channelContext, packet);

                    } else {

                        singleRet = send(channelContext, packet);

                    }

                    if (singleRet) {

                        ret = true;

                    }

                }

                return Boolean.valueOf(ret);

            } catch (Throwable e) {

                log.error(e.getMessage(), e);

            } finally {

                readLock.unlock();

            }

            return Boolean.valueOf(false);

        } finally {

            if (groupContext.isCluster() && !packet.isFromCluster()) {

                TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();


                if (tioClusterConfig.isCluster4user()) {


                    Tio.notifyClusterForUser(groupContext, userid, (Packet) packet);

                }

            }

        }

    }


    public static Boolean sendToToken(GroupContext groupContext, String token, Packet packet) {

        return Tio.sendToToken(groupContext, token, packet);

    }


    public static ChannelContext getChannelContextByUserIdResource(GroupContext groupContext, String userId, String userResource) {

        SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);

        if (null == setWithLock) {

            return null;

        }

        try {

            ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();

            readLock.lock();


            try {

                Set<ChannelContext> set = (Set<ChannelContext>) setWithLock.getObj();

                for (ChannelContext channelContext : set) {

                    Object resource = channelContext.getAttribute("resource");

                    if (null != resource && resource.equals(userResource)) {

                        return channelContext;

                    }

                }

                return null;

            } catch (Throwable e) {

                log.error(e.getMessage(), e);

                return null;

            } finally {

                readLock.unlock();

            }


        } catch (Exception e) {

            log.error(e.getMessage(), e);

            return null;

        }

    }


    public static Set<String> getChannelContextResources(GroupContext groupContext, String userId) {

        Set<String> resources = new HashSet<>();

        SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);

        if (null == setWithLock) {

            return null;

        }

        try {

            ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();

            readLock.lock();


            try {

                Set<ChannelContext> set = (Set<ChannelContext>) setWithLock.getObj();

                for (ChannelContext channelContext : set) {

                    Object resource = channelContext.getAttribute("resource");

                    resources.add(resource.toString());

                }

                return resources;

            } catch (Throwable e) {

                log.error(e.getMessage(), e);

                return resources;

            } finally {

                readLock.unlock();

            }


        } catch (Exception e) {

            log.error(e.getMessage(), e);

            return resources;

        }

    }


    public static void userOnline(final String connStr, final String userId, final ChannelContext channelContext) {

        ThreadUtil.executeInThread(new Callback() {


            @Override
            public void execute(Object obj) {

                IMBeanUtils.getRocketmqService().handleLogin(connStr);

                List<String> jidList = IMBeanUtils.getRedisService().queryUserRoomJidList(Integer.valueOf(userId));

                for (String jid : jidList) {

                    Tio.bindGroup(channelContext, jid);

                }

                List<ChatMessage> queue = IMBeanUtils.getMessageRepository().loadChatOffToJID(userId, true);

                ImPacket packet = null;

                for (ChatMessage chatMessage : queue) {


                    chatMessage.getMessageHead().setOffline(true);

                    packet = MessageFactory.convertToImPacket((AbstractMessage) chatMessage);

                    packet.setMessage(chatMessage);

                    if (null != packet) {

                        CoreService.bSend(channelContext, packet);

                    }

                }

            }

        });

    }


    public static void notifyUserConflict(ChannelContext channelContext, String connStr, String arg) {

        ErrorMessage loginConflict = new ErrorMessage();

        loginConflict.setMessageHead(MessageFactory.createMessageHead(channelContext));

        loginConflict.getMessageHead().setTo(connStr);

        loginConflict.setCode((short) -2);

        loginConflict.setArg(arg);

        ImPacket conflict = MessageFactory.createLoginConflict(loginConflict);

        Tio.send(channelContext, (Packet) conflict);

    }


    public static boolean userIsOnline(GroupContext groupContext, String userId) {

        SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);

        ImClusterServiceIfc clusterService = IMBeanUtils.getBeanManager().getImClusterService();

        return ((null != setWithLock && 0 < setWithLock.size()) || (null != clusterService && clusterService
                .userIsOnline(userId)));

    }

}


